package com.szzg10.thinking.chapter18;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.IntBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;

public class MappedIO {

	private static int NUMOFINTS = 40000;
	
	private static abstract class Tester {
		private String name;
		
		public Tester(String name) {
			this.name = name;
		}
		
		public void runTest() throws IOException {
			System.out.print(name + ": ");
			
			long start = System.nanoTime();
			test();
			double duration = System.nanoTime() - start;
			
			System.out.format("%.2f\n", duration/1.0e9);
		}
		
		public abstract void test() throws IOException;
	}
	
	private static Tester[] tests = {
		new Tester("Stream Write") {
			@Override
			public void test() throws IOException {
				File file = new File(BasicFileOutput.FILE_NAME_OUT);
				FileOutputStream fileOutputStream = new FileOutputStream(file);
				BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream);
				DataOutputStream dataOutputStream = new DataOutputStream(bufferedOutputStream);
				
				for(int i = 0; i < NUMOFINTS; i++) {
					dataOutputStream.writeInt(i);
				}
				
				dataOutputStream.close();
			}
		},
		new Tester("Mapped Write") {
			@Override
			public void test() throws IOException {
				File file = new File(BasicFileOutput.FILE_NAME_OUT);
				RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
				FileChannel channel = randomAccessFile.getChannel();
				
				MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 4 * NUMOFINTS);
				IntBuffer intBuffer = mappedByteBuffer.asIntBuffer();
				
				for(int i = 0; i < NUMOFINTS; i++) {
					intBuffer.put(i);
				}
				
				channel.close();
				randomAccessFile.close();
			}
			
		},
		new Tester("Stram Read") {
			@Override
			public void test() throws IOException {
				File file = new File(BasicFileOutput.FILE_NAME_OUT);
				FileInputStream fileInputStream = new FileInputStream(file);
				BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream);
				DataInputStream dataInputStream = new DataInputStream(bufferedInputStream);
				
				while(dataInputStream.available() > 0) {
					dataInputStream.readInt();
				}
				
				dataInputStream.close();
			}
		},
		new Tester("Mapped Read") {
			@Override
			public void test() throws IOException {
				File file = new File(BasicFileOutput.FILE_NAME_OUT);
				FileInputStream fileInputStream = new FileInputStream(file);
				FileChannel channel = fileInputStream.getChannel();
				
				MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_ONLY, 0, 4 * NUMOFINTS);
				IntBuffer intBuffer = mappedByteBuffer.asIntBuffer();
				while(intBuffer.hasRemaining()) {
					intBuffer.get();
				}
				
				channel.close();
				fileInputStream.close();
			}
		},
		new Tester("Stream Read/Write") {
			@Override
			public void test() throws IOException {
				File file = new File(BasicFileOutput.FILE_NAME_OUT);
				RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
				
				for(int i = 0; i < NUMOFINTS; i++) {
					randomAccessFile.writeInt(i);
				}
				
				randomAccessFile.seek(0);
				for(int i = 0; i< NUMOFINTS; i++) {
					randomAccessFile.read();
				}
				
				randomAccessFile.close();
			}
		},
		new Tester("Mapped Read/Write") {
			@Override
			public void test() throws IOException {
				File file = new File(BasicFileOutput.FILE_NAME_OUT);
				RandomAccessFile randomAccessFile = new RandomAccessFile(file, "rw");
				FileChannel channel = randomAccessFile.getChannel();
				MappedByteBuffer mappedByteBuffer = channel.map(FileChannel.MapMode.READ_WRITE, 0, 4 * NUMOFINTS);
				IntBuffer intBuffer = mappedByteBuffer.asIntBuffer();
				
				for(int i = 0; i < NUMOFINTS; i++) {
					intBuffer.put(i);
				}
				
				intBuffer.rewind();
				for(int i = 0; i < NUMOFINTS; i++) {
					intBuffer.get();
				}
				
				channel.close();
				randomAccessFile.close();
			}
			
		}
	};
	
	public static void main(String[] args) throws IOException {
		for(Tester element: tests) {
			element.runTest();
		}
	}
}
